package com.youlu.campus.web.order.service;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.base.exception.DuplicateOrderException;
import com.youlu.campus.entity.*;
import com.youlu.campus.entity.VO.req.DigitalCaPlaceOrder;
import com.youlu.campus.entity.ca.UserCa;
import com.youlu.campus.entity.commission.DistributorApply;
import com.youlu.campus.entity.commission.dto.DistributorChannelSharingCommissionOrderDTO;
import com.youlu.campus.entity.commission.dto.SharingCommissionOrderDTO;
import com.youlu.campus.entity.course.dto.GoodsOrderDTO;
import com.youlu.campus.entity.enums.SharingProfitOrderTypeEnum;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.activity.UserParticipateActivityInfoService;
import com.youlu.campus.service.ca.UserCaService;
import com.youlu.campus.service.commission.DigitalCaCommissionServiceService;
import com.youlu.campus.service.commission.DistributorChannelSharingCommissionService;
import com.youlu.campus.service.course.WechatPayService;
import com.youlu.campus.service.order.MerchantSharingProfitService;
import com.youlu.campus.service.wechat.WechatConfigInfoService;
import com.youlu.campus.service.wechat.WechatMerchantConfigService;
import com.youlu.campus.service.wechatpay.WechatPayCacheService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class DigitalCaOrderService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private ActivityService activityService;

    @Autowired
    private UserCaService userCaService;

    @Autowired
    private WechatConfigInfoService wechatConfigInfoService;

    @Autowired
    private WechatMerchantConfigService wechatMerchantConfigService;

    @Autowired
    private UserParticipateActivityInfoService userParticipateActivityInfoService;

    @Autowired
    private WechatPayService wechatPayService;

    @Autowired
    private WechatPayCacheService wechatPayCacheService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    private static final String DIGITAL_CA_ORDER_CACHE = "campus:digitalCaOrder:%s";

    /**
     * 下单限制频繁提交
     */
    private static final String PLACE_ORDER_LOCK = "campus:digitalCaOrder:lock:%s:user:%s:caType:%s:caLevel:%s";

    /**
     * 获取缓存中信息
     */
    private static final String PLACE_ORDER_CACHE = "campus:digitalCaOrder:receive:%s:user:%s:caType:%s:caLevel:%s";

    @Autowired
    private MerchantSharingProfitService merchantSharingProfitService;

    @Autowired
    private DigitalCaCommissionServiceService digitalCaCommissionServiceService;

    @Autowired
    private DistributorChannelSharingCommissionService distributorChannelSharingCommissionService;


    @Async
    public void initOrderDigitalCaCommission(String activityId){
        log.info("【电子证书订单分佣】activityId:{} ",activityId);
        Query query = new Query();
        query.addCriteria(Criteria.where("activityId").is(activityId));
        query.addCriteria(Criteria.where("status").is(1));
        List<DigitalCaOrder> caOrderList = this.mongoTemplate.find(query, DigitalCaOrder.class);
        if(CollectionUtils.isEmpty(caOrderList)){
            log.warn("【电子证书订单分佣】activityId:{} end 未查询到",activityId);

            return;
        }
        caOrderList.stream().forEach(digitalCaOrder -> {
            //结算佣金
            String userId = digitalCaOrder.getUserId();
            Integer sharingCommissionOrderAmount =0;
            //总订单金额减去分拥订单后金额
            UserInviteRecord userInviteRecord = digitalCaCommissionServiceService.inviterDistributor(userId, activityId);
            if(Objects.nonNull(userInviteRecord)&&userInviteRecord.getInviterDistributor()){
                DistributorApply distributorApply = digitalCaCommissionServiceService.findByDistributorId(userInviteRecord.getInvitedByUserId());
                if(Objects.nonNull(distributorApply)){
                    log.info("【电子证书订单分佣】orderNo:{},订单金额:{},分销员:{}",digitalCaOrder.getOrderNo(),
                            digitalCaOrder.getTotalPrice(),distributorApply.getName());
                    SharingCommissionOrderDTO sharingCommissionOrderDto = new SharingCommissionOrderDTO();
                    sharingCommissionOrderDto.setActivityId(activityId);
                    sharingCommissionOrderDto.setAppId(digitalCaOrder.getAppId());
                    sharingCommissionOrderDto.setActivityName(digitalCaOrder.getActivityName());
                    sharingCommissionOrderDto.setDistributorCommission(distributorApply.getDistributorCommission());
                    sharingCommissionOrderDto.setMerchantId(digitalCaOrder.getMerchantId());
                    sharingCommissionOrderDto.setMerchantName(digitalCaOrder.getMerchantName());
                    sharingCommissionOrderDto.setOrderNo(digitalCaOrder.getOrderNo());
                    sharingCommissionOrderDto.setPayTime(digitalCaOrder.getPayTime());
                    sharingCommissionOrderDto.setTransactionId(digitalCaOrder.getTransactionId());
                    sharingCommissionOrderDto.setTotalFee(digitalCaOrder.getTotalPrice());
                    sharingCommissionOrderDto.setCategory(SharingProfitOrderTypeEnum.DIGITAL_CA_ORDER.name());
                    sharingCommissionOrderAmount = digitalCaCommissionServiceService.doSharingCommissionAmount(sharingCommissionOrderDto);
                    log.info("【电子证书订单分佣】orderNo:{},订单金额:{},佣金:{}",digitalCaOrder.getOrderNo(),
                            digitalCaOrder.getTotalPrice(),sharingCommissionOrderAmount);
                }
            }
        });
        log.info("【电子证书订单分佣】activityId:{} end",activityId);


    }



    public DigitalCaOrder detail(String orderNo) {
        String orderNoCache = String.format(DIGITAL_CA_ORDER_CACHE, orderNo);
        String orderCache = stringRedisTemplate.opsForValue().get(orderNoCache);
        if (StringUtils.isNotBlank(orderCache)) {
            return JSON.parseObject(orderCache, DigitalCaOrder.class);
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("orderNo").is(orderNo));
        return this.mongoTemplate.findOne(query, DigitalCaOrder.class);

    }

    /**
     * 支付通知
     *
     * @param orderNo
     * @param transactionId
     */
    public void notifyPay(String orderNo, String transactionId) {
        log.info("【电子证书订单】DigitalCaOrderService.notifyPay  支付回调通知 orderNo->{}|transactionId->{}", orderNo,
                transactionId);
        Query query = new Query();
        query.addCriteria(Criteria.where("orderNo").is(orderNo));
        Update update = new Update();
        Date now = new Date();
        update.set("status", 1);
        update.set("payTime", now);
        update.set("updatedTime", now);
        update.set("transactionId", transactionId);
        UpdateResult updateResult = this.mongoTemplate.updateFirst(query, update, DigitalCaOrder.class);
        log.info("【电子证书订单】DigitalCaOrderService.notifyPay  支付回调通知更新结果 orderNo->{}|transactionId->{} result ->{}",
                orderNo,
                transactionId, updateResult.getModifiedCount());
        if (updateResult.getModifiedCount() > 0) {
            //结算佣金
            DigitalCaOrder digitalCaOrder = this.mongoTemplate.findOne(query, DigitalCaOrder.class);
            if (Objects.nonNull(digitalCaOrder)) {
                //结算佣金
                String activityId = digitalCaOrder.getActivityId();
                String userId = digitalCaOrder.getUserId();
                Integer sharingCommissionOrderAmount =0;
                Integer sharingCommissionChannelOrderAmount = 0;
                //总订单金额减去分拥订单后金额
                Integer orderAmountAfter = 0;
                UserInviteRecord userInviteRecord = digitalCaCommissionServiceService.inviterDistributor(userId, activityId);
                if(Objects.nonNull(userInviteRecord)&&userInviteRecord.getInviterDistributor()){
                    DistributorApply distributorApply = digitalCaCommissionServiceService.findByDistributorId(userInviteRecord.getInvitedByUserId());
                    if(Objects.nonNull(distributorApply)){
                        SharingCommissionOrderDTO sharingCommissionOrderDto = new SharingCommissionOrderDTO();
                        sharingCommissionOrderDto.setActivityId(activityId);
                        sharingCommissionOrderDto.setActivityName(digitalCaOrder.getActivityName());
                        sharingCommissionOrderDto.setAppId(digitalCaOrder.getAppId());
                        sharingCommissionOrderDto.setDistributorCommission(distributorApply.getDistributorCommission());
                        sharingCommissionOrderDto.setMerchantId(digitalCaOrder.getMerchantId());
                        sharingCommissionOrderDto.setMerchantName(digitalCaOrder.getMerchantName());
                        sharingCommissionOrderDto.setOrderNo(digitalCaOrder.getOrderNo());
                        sharingCommissionOrderDto.setPayTime(now);
                        sharingCommissionOrderDto.setTotalFee(digitalCaOrder.getTotalPrice());
                        sharingCommissionOrderDto.setTransactionId(transactionId);
                        sharingCommissionOrderDto.setCategory(SharingProfitOrderTypeEnum.DIGITAL_CA_ORDER.name());
                        sharingCommissionOrderAmount = digitalCaCommissionServiceService.doSharingCommissionAmount(sharingCommissionOrderDto);
                    }
                }else{
                    //渠道分拥
                    DistributorChannelSharingCommissionOrderDTO sharingCommissionOrderChannelDto = new DistributorChannelSharingCommissionOrderDTO();
                    sharingCommissionOrderChannelDto.setActivityId(digitalCaOrder.getActivityId());
                    sharingCommissionOrderChannelDto.setAppId(digitalCaOrder.getAppId());
                    sharingCommissionOrderChannelDto.setMerchantId(digitalCaOrder.getMerchantId());
                    sharingCommissionOrderChannelDto.setMerchantName(digitalCaOrder.getMerchantName());
                    sharingCommissionOrderChannelDto.setOrderNo(orderNo);
                    sharingCommissionOrderChannelDto.setPayTime(now);
                    sharingCommissionOrderChannelDto.setTotalFee(digitalCaOrder.getTotalPrice());
                    sharingCommissionOrderChannelDto.setTransactionId(transactionId);
                    sharingCommissionOrderChannelDto.setCategory(SharingProfitOrderTypeEnum.DIGITAL_CA_ORDER.name());
                    sharingCommissionOrderChannelDto.setUserId(digitalCaOrder.getUserId());
                    sharingCommissionOrderChannelDto.setMobile(digitalCaOrder.getMobile());
                    sharingCommissionOrderChannelDto.setName(digitalCaOrder.getName());
                    sharingCommissionOrderChannelDto.setUniversityId(digitalCaOrder.getUniversityId());
                    sharingCommissionOrderChannelDto.setUniversityName(digitalCaOrder.getUniversityName());
                    sharingCommissionChannelOrderAmount = distributorChannelSharingCommissionService.doSharingCommissionAmount(sharingCommissionOrderChannelDto);
                }

                if(sharingCommissionOrderAmount>0||sharingCommissionChannelOrderAmount>0){
                    orderAmountAfter= digitalCaOrder.getTotalPrice() - Math.addExact(sharingCommissionOrderAmount, sharingCommissionChannelOrderAmount);
                }else{
                    orderAmountAfter = digitalCaOrder.getTotalPrice();
                }
                OrderInfo sharingProfitOrder = new OrderInfo();
                sharingProfitOrder.setOrderNo(orderNo);
                sharingProfitOrder.setActivityId(activityId);
                sharingProfitOrder.setCategory(SharingProfitOrderTypeEnum.DIGITAL_CA_ORDER.name());
                sharingProfitOrder.setTotalFee(orderAmountAfter);
                sharingProfitOrder.setPayTime(now);
                sharingProfitOrder.setTransactionId(transactionId);
                sharingProfitOrder.setAppId(digitalCaOrder.getAppId());
                sharingProfitOrder.setMechantId(digitalCaOrder.getMerchantId());
                sharingProfitOrder.setMechantName(digitalCaOrder.getMerchantName());
                merchantSharingProfitService.doSharingProfit(sharingProfitOrder);
            }
            String orderNoCache = String.format(DIGITAL_CA_ORDER_CACHE, orderNo);
            String orderCache = stringRedisTemplate.opsForValue().get(orderNoCache);
            if (StringUtils.isNotBlank(orderCache)) {
                log.info("【电子证书订单】DigitalCaOrderService.notifyPay  支付回调通知更新订单状态成功修改缓存 orderNo->{}", orderNo);
                DigitalCaOrder digitalCaOrder2 = JSON.parseObject(orderCache, DigitalCaOrder.class);
                digitalCaOrder2.setStatus(1);
                stringRedisTemplate.opsForValue().set(orderNoCache, JSON.toJSONString(digitalCaOrder2), 10L,
                        TimeUnit.MINUTES);
                String cacheKey = String.format(PLACE_ORDER_CACHE, digitalCaOrder2.getActivityId(),
                        digitalCaOrder2.getUserId(),
                        digitalCaOrder2.getCaType(), digitalCaOrder2.getCaLevel());
                stringRedisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(digitalCaOrder2), 10L,
                        TimeUnit.MINUTES);

            }
        }


    }


    public Object placeOrder(DigitalCaPlaceOrder digitalCaPlaceOrder) {
        UserPO currentUser = digitalCaPlaceOrder.getCurrentUser();
        String caType = digitalCaPlaceOrder.getCaType();
        String caLevel = digitalCaPlaceOrder.getCaLevel();
        String activityId = digitalCaPlaceOrder.getActivityId();
        String userId = currentUser.getId();
        log.info("【电子证书订单】DigitalCaOrderService.placeOrder userId->{}|activityId->{}|caLevel->{}|caType->{}",
                userId, activityId, caLevel, caType);
        String lockKey = String.format(PLACE_ORDER_LOCK, activityId, userId, caType, caLevel);
        Boolean existKey = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", 2L, TimeUnit.SECONDS);
        if (Objects.nonNull(existKey) && !existKey) {
            throw new BusinessException("对不起哦，服务繁忙,稍后重试");
        }
        ActivityInfo activityInfo = activityService.findOne(activityId);
        if (Objects.isNull(activityInfo)) {
            throw new BusinessException("活动不存在");
        }
        UserParticipateActivityInfo user = userParticipateActivityInfoService.findByUserIdActivityId(userId,
                activityId);
        if (Objects.isNull(user)) {
            throw new BusinessException("未参与报名");
        }
        WechatConfigInfo wechatConfigInfo = wechatConfigInfoService.find(currentUser.getAppId());
        if (Objects.isNull(wechatConfigInfo) || Objects.isNull(wechatConfigInfo.getMerchantIds())
                ||CollectionUtils.isEmpty(wechatConfigInfo.getMerchantIds())) {
            log.error(":>>> 微信公众号配置错误:{}", currentUser.getAppId());
            throw new BusinessException("当前支付人数多,稍后重试");
        }
        // 随机
        String merchantId = wechatConfigInfo.getMerchantIds().get(random(wechatConfigInfo.getMerchantIds().size()));
        WechatMerchantConfig merchant = wechatMerchantConfigService.findByMerchantIdAndDecrypt(merchantId);
        if (Objects.isNull(merchant)) {
            log.error(":>>> 微信配置错误:{} ,商户号不存在,请联系管理员", merchantId);
            throw new BusinessException("微信支付异常,请联系在线客服");
        }
        Integer contestDigitalCaFee = activityInfo.getContestDigitalCaFee();
        if (contestDigitalCaFee == 0) {
            log.warn("【电子证书订单】DigitalCaOrderService.placeOrder userId->{}|activityId->{}", userId, activityId);
            throw new BusinessException("电子证书费用为0，无需支付");
        }
        UserCa userCa = userCaService.findByUserIdAndCaTypeAndCaLevel(userId, caType, caLevel, activityId);
        if (Objects.isNull(userCa)) {
            log.error("【电子证书订单】DigitalCaOrderService.placeOrder 未找到 " +
                            "当前证书等级 userId->{}|activityId->{}|caLevel->{}|caType->{}",
                    userId, activityId, caLevel, caType);
            throw new BusinessException("对不起，服务繁忙!请稍后再试哦！");
        }
        Date now = new Date();
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("caType").is(caType));
        query.addCriteria(Criteria.where("activityId").is(activityId));
        query.addCriteria(Criteria.where("status").is(1));
        boolean exists = this.mongoTemplate.exists(query, DigitalCaOrder.class);
        if(exists){
            log.error("【电子证书订单】电子证书已支付同类型证书 userId:{},caType:{},activityId:{}",userId,caType,activityId);
            throw new DuplicateOrderException("证书同类型的不需要重复支付");
        }
        query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("caType").is(caType));
        query.addCriteria(Criteria.where("caLevel").is(caLevel));
        query.addCriteria(Criteria.where("activityId").is(activityId));
        query.addCriteria(Criteria.where("status").is(1));
        DigitalCaOrder digitalCaOrder = this.mongoTemplate.findOne(query, DigitalCaOrder.class);
        if (Objects.nonNull(digitalCaOrder)) {
            log.error("【电子证书订单】DigitalCaOrderService.placeOrder 已支付，勿重复支付 " +
                            "userId->{}|activityId->{}|caLevel->{}|caType->{}",
                    userId, activityId, caLevel, caType);
            throw new DuplicateOrderException("已支付，勿重复支付！");
        }

        digitalCaOrder = new DigitalCaOrder();
        digitalCaOrder.setActivityId(activityId);
        digitalCaOrder.setActivityName(activityInfo.getName());
        digitalCaOrder.setCaType(caType);
        digitalCaOrder.setCaLevel(caLevel);
        digitalCaOrder.setClientIp(digitalCaPlaceOrder.getClientIp());
        digitalCaOrder.setMobile(user.getMobile());
        digitalCaOrder.setName(user.getName());
        digitalCaOrder.setAppId(currentUser.getAppId());
        digitalCaOrder.setOpenId(currentUser.getOpenId());
        digitalCaOrder.setPlatform(currentUser.getCurrentPlatform());
        digitalCaOrder.setMerchantId(merchant.getMerchantId());
        digitalCaOrder.setMerchantName(merchant.getMerchantName());
        digitalCaOrder.setUserId(userId);
        digitalCaOrder.setTotalPrice(contestDigitalCaFee);
        String orderNo = "D" + IdUtil.getSnowflake().nextIdStr();
        digitalCaOrder.setOrderNo(orderNo);
        digitalCaOrder.setCreatedTime(now);
        digitalCaOrder.setUpdatedTime(now);
        digitalCaOrder.setStatus(0);
        digitalCaOrder.setUniversityId(user.getUniversityId());
        digitalCaOrder.setUniversityName(user.getUniversityName());
        DigitalCaOrder orderDB = this.mongoTemplate.save(digitalCaOrder);
        String orderNoDB = orderDB.getOrderNo();
        log.warn("【电子证书订单】DigitalCaOrderService.placeOrder userId->{} 开始生成支付信息", userId, activityId);
        GoodsOrderDTO dto = new GoodsOrderDTO();
        dto.setOrderNo(orderNoDB);
        dto.setClientIp(orderDB.getClientIp());
        dto.setOpenId(orderDB.getOpenId());
        dto.setGoodsName(orderNoDB);
        dto.setPrice(orderDB.getTotalPrice());
        dto.setUserId(orderDB.getUserId());
        dto.setAppId(orderDB.getAppId());
        if (Objects.nonNull(activityInfo.getMerchantSharingProfit()) && activityInfo.getMerchantSharingProfit().getSharingProfit()) {
            dto.setProfitSharing(Boolean.TRUE);
        }
        merchant.setMerchantId(orderDB.getMerchantId());
        merchant.setMerchantName(orderDB.getMerchantName());
        Triple<Boolean, Object, String> result = wechatPayService.unifiedOrder(dto, merchant,
                digitalCaPlaceOrder.getUrl());
        if (!result.getLeft()) {
            throw new BusinessException(result.getRight());
        }
        Query queryCache = new Query().addCriteria(Criteria.where("orderNo").is(orderNoDB));
        boolean existC = mongoTemplate.exists(queryCache, WechatPayCache.class);
        if (!existC) {
            WechatPayCache cache = new WechatPayCache();
            cache.setOrderNo(orderNoDB);
            cache.setBizType("digitalCa");
            cache.setType("parent");
            wechatPayCacheService.create(cache);
        }
        String orderNoCache = String.format(DIGITAL_CA_ORDER_CACHE, orderNoDB);
        stringRedisTemplate.opsForValue().set(orderNoCache, JSON.toJSONString(orderDB), 10L, TimeUnit.MINUTES);
        return result.getMiddle();
    }

    private Integer random(int max) {
        if (max == 0) {
            return 0;
        }
        return ThreadLocalRandom.current().nextInt(max);
    }

}
